home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / ue312src.zip / SEARCH.C < prev    next >
C/C++ Source or Header  |  1993-04-20  |  31KB  |  1,279 lines

  1. /*
  2.  * The functions in this file implement commands that search in the forward
  3.  * and backward directions.
  4.  *
  5.  * (History comments formerly here have been moved to history.c)
  6.  */
  7.  
  8. #include <stdio.h>
  9. #include "estruct.h"
  10. #include "eproto.h"
  11. #include "edef.h"
  12. #include "elang.h"
  13.  
  14. static int    patlenadd;
  15. static int    lastchfjump, lastchbjump;
  16. static int    deltaf[HICHAR], deltab[HICHAR];
  17.  
  18. #if    MAGIC
  19. static int    group_count;
  20. static int    group_len[MAXGROUPS];
  21. static REGION    group_reg[MAXGROUPS];
  22. #endif
  23.  
  24. /*
  25.  * forwsearch -- Search forward.  Get a search string from the user, and
  26.  *    search for the string.  If found, reset the "." to be just after
  27.  *    the match string, and (perhaps) repaint the display.
  28.  */
  29. int PASCAL NEAR forwsearch(f, n)
  30. int f, n;    /* default flag / numeric argument */
  31. {
  32.     register int    status;
  33.  
  34.     /* If n is negative, search backwards.
  35.      * Otherwise proceed by asking for the search string.
  36.      */
  37.     if (n < 0)
  38.         return (backsearch(f, -n));
  39.  
  40.     /* Ask the user for the text of a pattern.  If the response is
  41.      * TRUE (responses other than FALSE are possible), search for
  42.      * pattern for up to n times, as long as the pattern is there
  43.      * to be found.
  44.      */
  45.     if ((status = readpattern(TEXT78, &pat[0], TRUE)) == TRUE)
  46.         status = forwhunt(f, n);
  47. /*                "Search" */
  48.  
  49.     return (status);
  50. }
  51.  
  52. /*
  53.  * forwhunt -- Search forward for a previously acquired search string.
  54.  *    If found, reset the "." to be just after the match string,
  55.  *    and (perhaps) repaint the display.
  56.  */
  57. int PASCAL NEAR forwhunt(f, n)
  58. int f, n;    /* default flag / numeric argument */
  59. {
  60.     register int    spoint = PTEND;
  61.     register int    status;
  62.  
  63.     if (n < 0)        /* search backwards */
  64.         return (backhunt(f, -n));
  65.  
  66.     /* Make sure a pattern exists, or that we didn't switch
  67.      * into MAGIC mode after we entered the pattern.
  68.      */
  69.     if (pat[0] == '\0') {
  70.         mlwrite(TEXT80);
  71. /*            "No pattern set" */
  72.         return FALSE;
  73.     }
  74.  
  75. #if    MAGIC
  76.     if ((curwp->w_bufp->b_mode & MDMAGIC) && mcpat[0].mc_type == MCNIL) {
  77.         if (!mcstr())
  78.             return FALSE;
  79.     }
  80. #endif
  81.  
  82.     /*
  83.      * Do one extra search to get us past our current
  84.      * match, if the search type has us at the start
  85.      * of a match, instead of after a match.
  86.      */
  87.     if (searchtype == SRBEGIN) {
  88.         spoint = PTBEG;
  89.         if (lastflag & CFSRCH)
  90.             n = (n > 2)? (n + 1): 2;
  91.     }
  92.  
  93. #if    MAGIC
  94.     if (magical && (curwp->w_bufp->b_mode & MDMAGIC))
  95.         status = mcscanner(FORWARD, spoint, n);
  96.     else
  97. #endif
  98.         status = scanner(FORWARD, spoint, n);
  99.  
  100.     /* Complain if not there.
  101.      */
  102.     if (status == FALSE)
  103.         mlwrite(TEXT79);
  104. /*            "Not found" */
  105.  
  106.     thisflag |= CFSRCH;
  107.     return (status);
  108. }
  109.  
  110. /*
  111.  * backsearch -- Reverse search.  Get a search string from the user, and
  112.  *    search, starting at "." and proceeding toward the front of the buffer.
  113.  *    If found "." is left pointing at the first character of the pattern
  114.  *    (the last character that was matched).
  115.  */
  116. int PASCAL NEAR backsearch(f, n)
  117. int f, n;    /* default flag / numeric argument */
  118. {
  119.     register int    status;
  120.  
  121.     /* If n is negative, search forwards.
  122.      * Otherwise proceed by asking for the search string.
  123.      */
  124.     if (n < 0)
  125.         return (forwsearch(f, -n));
  126.  
  127.     /* Ask the user for the text of a pattern.  If the response is
  128.      * TRUE (responses other than FALSE are possible), search for
  129.      * pattern for up to n times, as long as the pattern is there
  130.      * to be found.
  131.      */
  132.     if ((status = readpattern(TEXT81, &pat[0], TRUE)) == TRUE)
  133.         status = backhunt(f, n);
  134. /*                "Reverse search" */
  135.  
  136.     return (status);
  137. }
  138.  
  139. /*
  140.  * backhunt -- Reverse search for a previously acquired search string,
  141.  *    starting at "." and proceeding toward the front of the buffer.
  142.  *    If found "." is left pointing at the first character of the pattern
  143.  *    (the last character that was matched).
  144.  */
  145. int PASCAL NEAR backhunt(f, n)
  146. int f, n;    /* default flag / numeric argument */
  147. {
  148.     register int    spoint = PTBEG;
  149.     register int    status;
  150.  
  151.     if (n < 0)
  152.         return (forwhunt(f, -n));
  153.  
  154.     /* Make sure a pattern exists, or that we didn't switch
  155.      * into MAGIC mode after we entered the pattern.
  156.      */
  157.     if (tap[0] == '\0') {
  158.         mlwrite(TEXT80);
  159. /*            "No pattern set" */
  160.         return FALSE;
  161.     }
  162.  
  163. #if    MAGIC
  164.     if ((curwp->w_bufp->b_mode & MDMAGIC) && tapcm[0].mc_type == MCNIL) {
  165.         if (!mcstr())
  166.             return FALSE;
  167.     }
  168. #endif
  169.  
  170.     /*
  171.      * Do one extra search to get us past our current
  172.      * match, if the search type has us at the start
  173.      * of a match, instead of after a match.
  174.      */
  175.     if (searchtype == SREND) {
  176.         spoint = PTEND;
  177.         if (lastflag & CFSRCH)
  178.             n = (n > 2)? (n + 1): 2;
  179.     }
  180.  
  181. #if    MAGIC
  182.     if (magical && (curwp->w_bufp->b_mode & MDMAGIC))
  183.         status = mcscanner(REVERSE, spoint, n);
  184.     else
  185. #endif
  186.         status = scanner(REVERSE, spoint, n);
  187.  
  188.     /* Complain if not there.
  189.      */
  190.     if (status == FALSE)
  191.         mlwrite(TEXT79);
  192. /*            "Not found" */
  193.  
  194.     thisflag |= CFSRCH;
  195.     return (status);
  196. }
  197.  
  198. #if    MAGIC
  199. /*
  200.  * mcscanner -- Search for a meta-pattern in either direction.  If found,
  201.  *    reset the "." to be at the start or just after the match string,
  202.  *    and (perhaps) repaint the display.
  203.  */
  204. int PASCAL NEAR mcscanner(direct, beg_or_end, repeats)
  205. int    direct;        /* which way to go.*/
  206. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  207. int    repeats;    /* search repetitions.*/
  208. {
  209.     MC    *mcpatrn;    /* pointer into pattern */
  210.     LINE    *curline;    /* current line during scan */
  211.     int    curoff;        /* position within current line */
  212.  
  213.     /* If we are going in reverse, then the 'end' is actually
  214.      * the beginning of the pattern.  Toggle it.
  215.      */
  216.     beg_or_end ^= direct;
  217.  
  218.     /* Another directional problem: if we are searching forwards,
  219.      * use the pattern in mcpat[].  Otherwise, use the reversed
  220.      * pattern in tapcm[].
  221.      */
  222.     mcpatrn = (direct == FORWARD)? &mcpat[0]: &tapcm[0];
  223.  
  224.     /* Setup local scan pointers to global ".".
  225.      */
  226.     curline = curwp->w_dotp;
  227.     curoff  = curwp->w_doto;
  228.  
  229.     /* Scan each character until we hit the head link record.
  230.      */
  231.     while (!boundry(curline, curoff, direct)) {
  232.         /* Save the current position in case we need to
  233.          * restore it on a match, and initialize matchlen to
  234.          * zero in case we are doing a search for replacement.
  235.          */
  236.         matchline = curline;
  237.         matchoff = curoff;
  238.         matchlen = 0;
  239.  
  240. #if     WINDOW_MSWIN
  241.         {
  242.             static int  o = 0;
  243.             if (--o < 0) {
  244.             longop(TRUE);
  245.             o = 100;   /* to lower overhead, only 1/100
  246.                        calls to longop */
  247.             }
  248.         }
  249. #endif
  250.         if (amatch(mcpatrn, direct, &curline, &curoff)) {
  251.             /* A SUCCESSFULL MATCH!!!
  252.              * Flag that we have moved,
  253.              * reset the global "." pointers.
  254.              */
  255.             curwp->w_flag |= WFMOVE;
  256.             if (beg_or_end == PTEND)    /* at end of string */
  257.             {
  258.                 curwp->w_dotp = curline;
  259.                 curwp->w_doto = curoff;
  260.             }
  261.             else        /* at beginning of string */
  262.             {
  263.                 curwp->w_dotp = matchline;
  264.                 curwp->w_doto = matchoff;
  265.             }
  266.  
  267.             /* If we're heading in reverse, set the "match"
  268.              * pointers to the start of the string, for savematch().
  269.              */
  270.             if (direct == REVERSE) {
  271.                 matchline = curline;
  272.                 matchoff = curoff;
  273.             }
  274.  
  275.             if (savematch() == ABORT)
  276.                 return ABORT;
  277.  
  278.             /* Continue scanning if we haven't found
  279.              * the nth match.
  280.              */
  281.             if (--repeats <= 0)
  282.                 return TRUE;
  283.         }
  284.  
  285.         /* Advance the cursor.
  286.          */
  287.         nextch(&curline, &curoff, direct);
  288.     }
  289.  
  290.     return FALSE;    /* We could not find a match.*/
  291. }
  292.  
  293. /*
  294.  * amatch -- Search for a meta-pattern in either direction.  Based on the
  295.  *    recursive routine amatch() (for "anchored match") in
  296.  *    Kernighan & Plauger's "Software Tools".
  297.  */
  298. int PASCAL NEAR    amatch(mcptr, direct, pcwline, pcwoff)
  299. register MC    *mcptr;        /* pattern to scan for */
  300. int        direct;        /* which way to go.*/
  301. LINE        **pcwline;    /* current line during scan */
  302. int        *pcwoff;    /* position within current line */
  303. {
  304.     LINE    *curline;    /* current line during scan */
  305.     int    curoff;        /* position within current line */
  306.     int    pre_matchlen;    /* matchlen before a recursive amatch() call.*/
  307.     int    cl_matchlen;    /* number of chars matched in a local closure.*/
  308.     int    cl_min;        /* minimum number of chars matched in closure.*/
  309.     int    cl_type;    /* Which closure type?.*/
  310.  
  311.     /* Set up local scan pointers to ".",
  312.      * and set up our local character counting
  313.      * variable, which can correct matchlen on
  314.      * a failed partial match.
  315.      */
  316.     curline = *pcwline;
  317.     curoff = *pcwoff;
  318.     cl_matchlen = 0;
  319.  
  320.     /*
  321.      * Loop through the meta-pattern, laughing all the way.
  322.      */
  323.     while (mcptr->mc_type != MCNIL) {
  324.         /* Is the current meta-character modified
  325.          * by a closure?
  326.          */
  327.         if (cl_type = (mcptr->mc_type & ALLCLOS)) {
  328.             if (cl_type == ZEROONE)
  329.             {
  330.                 cl_min = 0;
  331.  
  332.                 if (mceq(nextch(&curline, &curoff, direct), mcptr))
  333.                 {
  334.                     nextch(&curline, &curoff, direct);
  335.                     cl_matchlen++;
  336.                 }
  337.             }
  338.             else
  339.             {
  340.                 /* Minimum number of characters that may
  341.                  * match is 0 or 1.
  342.                  */
  343.                 cl_min = (cl_type == CLOSURE_1);
  344.  
  345.                 /* Match as many characters as possible
  346.                  * against the current meta-character.
  347.                  */
  348.                 while (mceq(nextch(&curline, &curoff, direct), mcptr)) {
  349.                     cl_matchlen++;
  350.                 }
  351.             }
  352.  
  353.             /* We are now at the character that made us
  354.              * fail.  Try to match the rest of the pattern.
  355.              * Shrink the closure by one for each failure.
  356.              */
  357.             mcptr++;
  358.             matchlen += cl_matchlen;
  359.  
  360.             for (;;)
  361.             {
  362.                 if (cl_matchlen < cl_min) {
  363.                     matchlen -= cl_matchlen;
  364.                     return FALSE;
  365.                 }
  366.  
  367.                 nextch(&curline, &curoff, direct ^ REVERSE);
  368.                 pre_matchlen = matchlen;
  369.                 if (amatch(mcptr, direct, &curline, &curoff))
  370.                     goto success;
  371.                 matchlen = pre_matchlen - 1;
  372.                 cl_matchlen--;
  373.             }
  374.         }
  375.         else if (mcptr->mc_type == GRPBEG) {
  376.             group_reg[mcptr->u.group_no].r_offset = curoff;
  377.             group_reg[mcptr->u.group_no].r_linep = curline;
  378.             group_reg[mcptr->u.group_no].r_size = (direct == FORWARD)? -matchlen: matchlen;
  379.         }
  380.         else if (mcptr->mc_type == GRPEND) {
  381.             group_len[mcptr->u.group_no] = (direct == FORWARD)? matchlen: -matchlen;
  382.         }
  383.         else if (mcptr->mc_type == BOL) {
  384.             if (curoff != 0)
  385.                 return FALSE;
  386.         }
  387.         else if (mcptr->mc_type == EOL) {
  388.             if (curoff != lused(curline))
  389.                 return FALSE;
  390.         }
  391.         else
  392.         {
  393.             /* A character to compare against
  394.              * the meta-character equal function.
  395.              * If we still match, increment the length.
  396.              */
  397.             if (!mceq(nextch(&curline, &curoff, direct), mcptr))
  398.                 return FALSE;
  399.  
  400.             matchlen++;
  401.         }
  402.  
  403.         mcptr++;
  404.     }            /* End of mcptr loop.*/
  405.  
  406.     /* A SUCCESSFULL MATCH!!!
  407.      * Reset the "." pointers.
  408.      */
  409. success:
  410.     *pcwline = curline;
  411.     *pcwoff  = curoff;
  412.     return (TRUE);
  413. }
  414. #endif
  415.  
  416. /*
  417.  * scanner -- Search for a pattern in either direction.  If found,
  418.  *    reset the "." to be at the start or just after the match string,
  419.  *    and (perhaps) repaint the display.
  420.  *    Fast version using simplified version of Boyer and Moore
  421.  *    Software-Practice and Experience, vol 10, 501-506 (1980)
  422.  */
  423. int PASCAL NEAR scanner(direct, beg_or_end, repeats)
  424. int    direct;        /* which way to go.*/
  425. int    beg_or_end;    /* put point at beginning or end of pattern.*/
  426. int    repeats;    /* search repetitions.*/
  427. {
  428.     register int    c;        /* character at current position */
  429.     register char    *patptr;    /* pointer into pattern */
  430.     char    *patrn;            /* string to scan for */
  431.     LINE    *curline;        /* current line during scan */
  432.     int    curoff;            /* position within current line */
  433.     LINE    *scanline;        /* current line during scanning */
  434.     int    scanoff;        /* position in scanned line */
  435.     int    jump;            /* next offset */
  436.  
  437.     /* If we are going in reverse, then the 'end' is actually
  438.      * the beginning of the pattern.  Toggle it.
  439.      */
  440.     beg_or_end ^= direct;
  441.  
  442.     /* Another directional problem: if we are searching
  443.      * forwards, use the pattern in pat[], and the jump
  444.      * size in lastchfjump.  Otherwise, use the reversed
  445.      * pattern in tap[], and the jump size of lastchbjump.
  446.      */
  447.     if (direct == FORWARD) {
  448.         patrn = (char *)&pat[0];
  449.         jump = lastchfjump;
  450.     }
  451.     else
  452.     {
  453.         patrn = (char *)&tap[0];
  454.         jump = lastchbjump;
  455.     }
  456.  
  457.     /* Set up local pointers to global ".".
  458.      */
  459.     curline = curwp->w_dotp;
  460.     curoff = curwp->w_doto;
  461.  
  462.     /* Scan each character until we hit the head link record.
  463.      * Get the character resolving newlines, offset
  464.      * by the pattern length, i.e. the last character of the
  465.      * potential match.
  466.      */
  467.     if (!fbound(patlenadd, &curline, &curoff, direct)) {
  468.         do
  469.         {
  470.             /* Set up the scanning pointers, and save
  471.              * the current position in case we match
  472.              * the search string at this point.
  473.              */
  474.             scanline = matchline = curline;
  475.             scanoff  = matchoff  = curoff;
  476.  
  477.             patptr = patrn;
  478.  
  479.             /* Scan through the pattern for a match.
  480.              */
  481.             while ((c = (unsigned char)(*patptr++)) != '\0')
  482.                 if (!eq(c, nextch(&scanline, &scanoff, direct)))
  483.                     goto fail;
  484.  
  485.             /* A SUCCESSFULL MATCH!!!
  486.              * Flag that we have moved and reset the
  487.              * global "." pointers.
  488.              */
  489.             curwp->w_flag |= WFMOVE;
  490.             if (beg_or_end == PTEND)    /* at end of string */
  491.             {
  492.                 curwp->w_dotp = scanline;
  493.                 curwp->w_doto = scanoff;
  494.             }
  495.             else        /* at beginning of string */
  496.             {
  497.                 curwp->w_dotp = matchline;
  498.                 curwp->w_doto = matchoff;
  499.             }
  500.  
  501.             /* If we're heading in reverse, set the "match"
  502.              * pointers to the start of the string, for savematch().
  503.              */
  504.             if (direct == REVERSE) {
  505.                 matchline = scanline;
  506.                 matchoff = scanoff;
  507.             }
  508.  
  509.             if (savematch() == ABORT)
  510.                 return ABORT;
  511.  
  512.             /* Continue scanning if we haven't found
  513.              * the nth match.
  514.              */
  515.             if (--repeats <= 0)
  516.                 return (TRUE);
  517.             else
  518.             {
  519.                 curline = scanline;
  520.                 curoff = scanoff;
  521.             }
  522.  
  523. fail:;            /* continue to search */
  524.         } while (!fbound(jump, &curline, &curoff, direct));
  525.     }
  526.  
  527.     return FALSE;    /* We could not find a match */
  528. }
  529.  
  530. /*
  531.  * fbound -- Return information depending on whether we have hit a boundry
  532.  *    (and may therefore search no further) or if a trailing character
  533.  *    of the search string has been found.  See boundry() for search
  534.  *    restrictions.
  535.  */
  536. int PASCAL NEAR    fbound(jump, pcurline, pcuroff, dir)
  537. LINE    **pcurline;
  538. int    *pcuroff, dir, jump;
  539. {
  540.     register int    spare, curoff;
  541.     register LINE    *curline;
  542.  
  543.     curline = *pcurline;
  544.     curoff = *pcuroff;
  545.  
  546.     if (dir == FORWARD) {
  547.         while (jump != 0) {
  548. #if    WINDOW_MSWIN
  549.             {
  550.                 static int    o = 0;
  551.                 if (--o < 0) {
  552.                     longop(TRUE);
  553.                     o = 100;   /* to lower overhead, only 1/100
  554.                             calls to longop */
  555.                 }
  556.             }
  557. #endif
  558.             curoff += jump;
  559.             spare = curoff - lused(curline);
  560.  
  561.             if (curline == curbp->b_linep)
  562.                 return (TRUE);    /* hit end of buffer */
  563.  
  564.             while (spare > 0) {
  565.                 curline = lforw(curline);/* skip to next line */
  566.                 curoff = spare - 1;
  567.                 spare = curoff - lused(curline);
  568.                 if (curline == curbp->b_linep)
  569.                     return (TRUE);    /* hit end of buffer */
  570.             }
  571.  
  572.             if (spare == 0)
  573.                 jump = deltaf[(int) '\r'];
  574.             else
  575.                 jump = deltaf[(int) lgetc(curline, curoff)];
  576.         }
  577.  
  578.         /* The last character matches, so back up to start
  579.          * of possible match.
  580.          */
  581.         curoff -= patlenadd;
  582.  
  583.         while (curoff < 0) {
  584.             curline = lback(curline);    /* skip back a line */
  585.             curoff += lused(curline) + 1;
  586.         }
  587.     }
  588.     else            /* Reverse.*/
  589.     {
  590.         jump++;        /* allow for offset in reverse */
  591.         while (jump != 0) {
  592. #if    WINDOW_MSWIN
  593.             {
  594.                 static int    o = 0;
  595.                 if (--o < 0) {
  596.                     longop(TRUE);
  597.                     o = 100;   /* to lower overhead, only 1/100
  598.                             calls to longop */
  599.                 }
  600.             }
  601. #endif
  602.             curoff -= jump;
  603.             while (curoff < 0) {
  604.                 curline = lback(curline);    /* skip back a line */
  605.                 curoff += lused(curline) + 1;
  606.                 if (curline == curbp->b_linep)
  607.                     return (TRUE);    /* hit end of buffer */
  608.             }
  609.  
  610.             if (curoff == lused(curline))
  611.                 jump = deltab[(int) '\r'];
  612.             else
  613.                 jump = deltab[(int) lgetc(curline, curoff)];
  614.         }
  615.  
  616.         /* The last character matches, so back up to start
  617.          * of possible match.
  618.          */
  619.         curoff += patlenadd + 1;
  620.         spare = curoff - lused(curline);
  621.         while (spare > 0) {
  622.             curline = lforw(curline);    /* skip back a line */
  623.             curoff = spare - 1;
  624.             spare = curoff - lused(curline);
  625.         }
  626.     }
  627.  
  628.     *pcurline = curline;
  629.     *pcuroff = curoff;
  630.     return FALSE;
  631. }
  632.  
  633. /*
  634.  * setjtable -- Settting up search delta forward and delta backward
  635.  *    tables.  The reverse search string and string lengths are
  636.  *    set here, for table initialization and for substitution
  637.  *    purposes.  The default for any character to jump is the
  638.  *    pattern length.
  639.  */
  640. VOID PASCAL NEAR setjtable()
  641. {
  642.     int    i;
  643.  
  644.     strcpy(tap, pat);
  645.     patlenadd = (matchlen = strlen(strrev(tap))) - 1;
  646.  
  647.     for (i = 0; i < HICHAR; i++) {
  648.         deltaf[i] = matchlen;
  649.         deltab[i] = matchlen;
  650.     }
  651.  
  652.     /* Now put in the characters contained
  653.      * in the pattern, duplicating the CASE.
  654.      */
  655.     for (i = 0; i < patlenadd; i++) {
  656.         if (isletter(pat[i]))
  657.             deltaf[(unsigned char) chcase(pat[i])]
  658.              = patlenadd - i;
  659.         deltaf[pat[i]] = patlenadd - i;
  660.   
  661.         if (isletter(tap[i]))
  662.             deltab[chcase(tap[i])]
  663.              = patlenadd - i;
  664.         deltab[tap[i]] = patlenadd - i;
  665.     }
  666.  
  667.     /* The last character will have the pattern length
  668.      * unless there are duplicates of it.  Get the number to
  669.      * jump from the arrays delta, and overwrite with zeroes
  670.      * in delta duplicating the CASE.
  671.      *
  672.      * The last character is, of course, the first character
  673.      * of the reversed string.
  674.      */
  675.     lastchfjump = patlenadd + deltaf[tap[0]];
  676.     lastchbjump = patlenadd + deltab[pat[0]];
  677.   
  678.     if (isletter(tap[0]))
  679.         deltaf[(unsigned char) chcase(tap[0])] = 0;
  680.     deltaf[tap[0]] = 0;
  681.   
  682.     if (isletter(pat[0]))
  683.         deltab[(unsigned char) chcase(pat[0])] = 0;
  684.     deltab[pat[0]] = 0;
  685. }
  686.  
  687. /*
  688.  * eq -- Compare two characters.  The "bc" comes from the buffer, "pc"
  689.  *    from the pattern.  If we are not in EXACT mode, fold out the case.
  690.  */
  691. int PASCAL NEAR eq(bc, pc)
  692. register unsigned char bc;
  693. register unsigned char pc;
  694. {
  695.     if ((curwp->w_bufp->b_mode & MDEXACT) == 0) {
  696.         if (is_lower(bc))
  697.             bc = chcase(bc);
  698.  
  699.         if (is_lower(pc))
  700.             pc = chcase(pc);
  701.     }
  702.  
  703.     return (bc == pc);
  704. }
  705.  
  706. /*
  707.  * readpattern -- Read a pattern.  Stash it in apat.  If it is the
  708.  *    search string (which means that the global variable pat[]
  709.  *    has been passed in), create the reverse pattern and the magic
  710.  *    pattern, assuming we are in MAGIC mode (and #defined that way).
  711.  *
  712.  *    Apat is not updated if the user types in an empty line.  If
  713.  *    the user typed an empty line, and there is no old pattern, it is
  714.  *    an error.  Display the old pattern, in the style of Jeff Lomicka.
  715.  *    There is some do-it-yourself control expansion.  Change to using
  716.  *    <META> to delimit the end-of-pattern to allow <NL>s in the search
  717.  *    string. 
  718.  */
  719. int PASCAL NEAR readpattern(prompt, apat, srch)
  720. char    *prompt;
  721. char    apat[];
  722. int    srch;
  723. {
  724.     register int    status;
  725.     char        tpat[NPAT+20];
  726.  
  727.     mlprompt(prompt, apat, sterm);
  728.  
  729.     /* Read a pattern.  Either we get one,
  730.      * or we just get the META charater, and use the previous pattern.
  731.      * Then, if it's the search string, create the delta tables.
  732.      * *Then*, make the meta-pattern, if we are defined that way.
  733.      */
  734.     if ((status = nextarg(NULL, tpat, NPAT, sterm)) == TRUE) {
  735.         lastflag &= ~CFSRCH;
  736.         strcpy(apat, tpat);
  737.  
  738.         if (srch)
  739.             setjtable();
  740.     }
  741.     else if (status == FALSE && apat[0] != 0)    /* Old one */
  742.         status = TRUE;
  743.  
  744. #if    MAGIC
  745.     /* Only make the meta-pattern if in magic mode, since the
  746.      * pattern in question might have an invalid meta combination.
  747.      */
  748.     if (status == TRUE)
  749.         if ((curwp->w_bufp->b_mode & MDMAGIC) == 0) {
  750.             mcclear();
  751.             rmcclear();
  752.         }
  753.         else
  754.             status = srch? mcstr(): rmcstr();
  755. #endif
  756.     return (status);
  757. }
  758.  
  759. /*
  760.  * savematch -- We found the pattern?  Let's save it away.
  761.  */
  762. int PASCAL NEAR savematch()
  763. {
  764.     register int    j;
  765.     REGION        tmpreg;
  766.  
  767. #if    (TURBO || ZTC) && (DOS16M == 0)
  768.     /* For those compilers whose reallocs() allow allocing memory
  769.      * even when the pointer passed in is NULL.
  770.      */
  771.     if ((patmatch = realloc(patmatch, matchlen + 1)) == NULL) {
  772.         mlabort(TEXT94);
  773. /*            "%%Out of memory" */
  774.         return ABORT;
  775.     }
  776. #else
  777.  
  778.     if (patmatch != NULL)
  779.         free(patmatch);
  780.  
  781.     if ((patmatch = malloc(matchlen + 1)) == NULL) {
  782.         mlabort(TEXT94);
  783. /*            "%%Out of memory" */
  784.         return ABORT;
  785.     }
  786. #endif
  787.  
  788.     tmpreg.r_offset = matchoff;
  789.     tmpreg.r_linep = matchline;
  790.     tmpreg.r_size = matchlen;
  791.  
  792. #if    MAGIC == 0
  793.     regtostr(patmatch, &tmpreg);
  794. #else
  795.     /*
  796.      * Save the groups.
  797.      */
  798.     grpmatch[0] = regtostr(patmatch, &tmpreg);
  799.  
  800.     for (j = 1; j <= group_count; j++)
  801.     {
  802.         group_reg[j].r_size += group_len[j];
  803.  
  804. #if    (TURBO || ZTC) && (DOS16M == 0)
  805.         /* For those compilers whose reallocs() allow allocating
  806.          * memory even when the pointer passed in is NULL.
  807.          */
  808.         if ((grpmatch[j] = realloc(grpmatch[j], group_reg[j].r_size + 1)) == NULL) {
  809.             mlabort(TEXT94);
  810. /*            "%%Out of memory" */
  811.             return ABORT;
  812.         }
  813. #else
  814.         if (grpmatch[j] != NULL)
  815.             free(grpmatch[j]);
  816.  
  817.         if ((grpmatch[j] = malloc(group_reg[j].r_size + 1)) == NULL) {
  818.             mlabort(TEXT94);
  819. /*            "%%Out of memory" */
  820.             return ABORT;
  821.         }
  822. #endif
  823.         regtostr(grpmatch[j], &group_reg[j]);
  824.     }
  825. #endif
  826.     return TRUE;
  827. }
  828.  
  829. /*
  830.  * boundry -- Return information depending on whether we may search no
  831.  *    further.  Beginning of file and end of file are the obvious
  832.  *    cases, but we may want to add further optional boundry restrictions
  833.  *    in future, a' la VMS EDT.  At the moment, just return (TRUE) or
  834.  *    FALSE depending on if a boundry is hit (ouch).
  835.  */
  836. int PASCAL NEAR boundry(curline, curoff, dir)
  837. LINE    *curline;
  838. int    curoff, dir;
  839. {
  840.     register int    border;
  841.  
  842.     if (dir == FORWARD) {
  843.         border = (curoff == lused(curline)) &&
  844.              (lforw(curline) == curbp->b_linep);
  845.     }
  846.     else
  847.     {
  848.         border = (curoff == 0) &&
  849.              (lback(curline) == curbp->b_linep);
  850.     }
  851.     return (border);
  852. }
  853.  
  854. /*
  855.  * nextch -- retrieve the next/previous character in the buffer,
  856.  *    and advance/retreat the point.
  857.  *    The order in which this is done is significant, and depends
  858.  *    upon the direction of the search.  Forward searches look at
  859.  *    the current character and move, reverse searches move and
  860.  *    look at the character.
  861.  */
  862. int PASCAL NEAR nextch(pcurline, pcuroff, dir)
  863. LINE    **pcurline;
  864. int    *pcuroff;
  865. int    dir;
  866. {
  867.     register LINE    *curline;
  868.     register int    curoff;
  869.     register int    c;
  870.  
  871.     curline = *pcurline;
  872.     curoff = *pcuroff;
  873.  
  874.     if (dir == FORWARD) {
  875.         if (curoff == lused(curline))        /* if at EOL */
  876.         {
  877.             curline = lforw(curline);    /* skip to next line */
  878.             curoff = 0;
  879.             c = '\r';            /* and return a <NL> */
  880.         }
  881.         else
  882.             c = lgetc(curline, curoff++);    /* get the char */
  883.     }
  884.     else            /* Reverse.*/
  885.     {
  886.         if (curoff == 0) {
  887.             curline = lback(curline);
  888.             curoff = lused(curline);
  889.             c = '\r';
  890.         }
  891.         else
  892.             c = lgetc(curline, --curoff);
  893.     }
  894.     *pcurline = curline;
  895.     *pcuroff = curoff;
  896.  
  897.     return (c);
  898. }
  899.  
  900. #if    MAGIC
  901. /*
  902.  * mcstr -- Set up the 'magic' array.  The closure symbol is taken as
  903.  *    a literal character when (1) it is the first character in the
  904.  *    pattern, and (2) when preceded by a symbol that does not allow
  905.  *    closure, such as beginning or end of line symbol, or another
  906.  *    closure symbol.
  907.  *
  908.  *    Coding comment (jmg):  yes, i know i have gotos that are, strictly
  909.  *    speaking, unnecessary.  But right now we are so cramped for
  910.  *    code space that i will grab what i can in order to remain
  911.  *    within the 64K limit.  C compilers actually do very little
  912.  *    in the way of optimizing - they expect you to do that.
  913.  */
  914. int PASCAL NEAR mcstr()
  915. {
  916.     MC    *mcptr, *rtpcm;
  917.     char    *patptr;
  918.     int    pchr;
  919.     int    status = TRUE;
  920.     int    does_closure = FALSE;
  921.     int    mj = 0;
  922.     int    group_stack[MAXGROUPS];
  923.     int    stacklevel = 0;
  924.  
  925.     /* If we had metacharacters in the MC array previously,
  926.      * free up any bitmaps that may have been allocated, and
  927.      * reset magical.
  928.      */
  929.     if (magical)
  930.         mcclear();
  931.  
  932.     mcptr = &mcpat[0];
  933.     patptr = (char *)&pat[0];
  934.  
  935.     while ((pchr = *patptr) && status) {
  936.         switch (pchr) {
  937.             case MC_CCL:
  938.                 status = cclmake(&patptr, mcptr);
  939.                 magical = TRUE;
  940.                 does_closure = TRUE;
  941.                 break;
  942.  
  943.             case MC_BOL:
  944.                 /* If the BOL character isn't the
  945.                  * first in the pattern, we assume
  946.                  * it's a literal instead.
  947.                  */
  948.                 if (mj != 0)
  949.                     goto litcase;
  950.  
  951.                 mcptr->mc_type = BOL;
  952.                 magical = TRUE;
  953.                 break;
  954.  
  955.             case MC_EOL:
  956.                 /* If the EOL character isn't the
  957.                  * last in the pattern, we assume
  958.                  * it's a literal instead.
  959.                  */
  960.                 if (*(patptr + 1) != '\0')
  961.                     goto litcase;
  962.  
  963.                 mcptr->mc_type = EOL;
  964.                 magical = TRUE;
  965.                 break;
  966.  
  967.             case MC_ANY:
  968.                 mcptr->mc_type = ANY;
  969.                 magical = TRUE;
  970.                 does_closure = TRUE;
  971.                 break;
  972.  
  973.             case MC_CLOSURE:
  974.             case MC_CLOSURE_1:
  975.             case MC_ZEROONE:
  976.  
  977.                 /* Does the closure symbol mean closure here?
  978.                  * If so, back up to the previous element
  979.                  * and indicate it is enclosed.
  980.                  */
  981.                 if (does_closure == FALSE)
  982.                     goto litcase;
  983.                 mj--;
  984.                 mcptr--;
  985.                 if (pchr == MC_CLOSURE)
  986.                     mcptr->mc_type |= CLOSURE;
  987.                 else if (pchr == MC_CLOSURE_1)
  988.                     mcptr->mc_type |= CLOSURE_1;
  989.                 else
  990.                     mcptr->mc_type |= ZEROONE;
  991.  
  992.                 magical = TRUE;
  993.                 does_closure = FALSE;
  994.                 break;
  995.  
  996.             case MC_ESC:
  997.  
  998.                 /* No break between here and LITCHAR if
  999.                  * the next character is to be taken literally.
  1000.                  */
  1001.                 magical = TRUE;
  1002.                 pchr = *++patptr;
  1003.                 if (pchr == MC_GRPBEG) {
  1004.                     /* Start of a group.  Indicate it, and
  1005.                      * set magical.
  1006.                      */
  1007.                     if (++group_count < MAXGROUPS) {
  1008.                         mcptr->mc_type = GRPBEG;
  1009.                         mcptr->u.group_no = group_count;
  1010.                         group_stack[stacklevel++] = group_count;
  1011.                         does_closure = FALSE;
  1012.                     }
  1013.                     else
  1014.                     {
  1015.                         mlwrite(TEXT221);
  1016. /*                            "Too many groups" */
  1017.                         status = FALSE;
  1018.                     }
  1019.                     break;
  1020.                 }
  1021.                 else if (pchr == MC_GRPEND) {
  1022.                     /* If we've no groups to close, assume
  1023.                      * a literal character.  Otherwise,
  1024.                      * indicate the end of a group.
  1025.                      */
  1026.                     if (stacklevel > 0) {
  1027.                         mcptr->u.group_no = group_stack[--stacklevel];
  1028.                         mcptr->mc_type = GRPEND;
  1029.                         does_closure = FALSE;
  1030.                         break;
  1031.                     }
  1032.                 }
  1033.                 else if (pchr == '\0') {
  1034.                     pchr = '\\';
  1035.                     --patptr;
  1036.                 }
  1037.             default:
  1038. litcase:            mcptr->mc_type = LITCHAR;
  1039.                 mcptr->u.lchar = pchr;
  1040.                 does_closure = TRUE;
  1041.                 break;
  1042.         }        /* End of switch.*/
  1043.         mcptr++;
  1044.         patptr++;
  1045.         mj++;
  1046.     }        /* End of while.*/
  1047.  
  1048.     /* Close off the meta-string, then set up the reverse array,
  1049.      * if the status is good.
  1050.      *
  1051.      * If the status is not good, nil out the meta-pattern.
  1052.      * Even if the status is bad from the cclmake() routine,
  1053.      * the bitmap for that member is guaranteed to be freed.
  1054.      * So we stomp a MCNIL value there, and call mcclear()
  1055.      * to free any other bitmaps.
  1056.      *
  1057.      * Please note the structure assignment - your compiler may
  1058.      * not like that.
  1059.      */
  1060.     mcptr->mc_type = MCNIL;
  1061.     if (stacklevel) {
  1062.         status = FALSE;
  1063.         mlwrite(TEXT222);
  1064. /*            "Group not ended"    */
  1065.     }
  1066.  
  1067.     if (status) {
  1068.         rtpcm = &tapcm[0];
  1069.         while (--mj >= 0) {
  1070. #if    LATTICE
  1071.             movmem(--mcptr, rtpcm, sizeof (MC));
  1072. #else
  1073.             *rtpcm = *--mcptr;
  1074. #endif
  1075.             rtpcm++;
  1076.         }
  1077.         rtpcm->mc_type = MCNIL;
  1078.     }
  1079.     else
  1080.     {
  1081.         (--mcptr)->mc_type = MCNIL;
  1082.         mcclear();
  1083.     }
  1084.  
  1085.     return (status);
  1086. }
  1087.  
  1088. /*
  1089.  * mcclear -- Free up any CCL bitmaps, and MCNIL the MC search arrays.
  1090.  */
  1091. VOID PASCAL NEAR mcclear()
  1092. {
  1093.     register MC    *mcptr;
  1094.     register int    j;
  1095.  
  1096.     mcptr = &mcpat[0];
  1097.  
  1098.     while (mcptr->mc_type != MCNIL) {
  1099.         if ((mcptr->mc_type == CCL) || (mcptr->mc_type == NCCL))
  1100.             free(mcptr->u.cclmap);
  1101.         mcptr++;
  1102.     }
  1103.     mcpat[0].mc_type = tapcm[0].mc_type = MCNIL;
  1104.  
  1105.     /*
  1106.      * Remember that grpmatch[0] == patmatch.
  1107.      */
  1108.     for (j = 0; j < MAXGROUPS; j++) {
  1109.         if (grpmatch[j] != NULL) {
  1110.             free(grpmatch[j]);
  1111.             grpmatch[j] = NULL;
  1112.         }
  1113.     }
  1114.     patmatch = NULL;
  1115.     group_count = 0;
  1116.     magical = FALSE;
  1117. }
  1118.  
  1119. /*
  1120.  * mceq -- meta-character equality with a character.  In Kernighan & Plauger's
  1121.  *    Software Tools, this is the function omatch(), but i felt there were
  1122.  *    too many functions with the 'match' name already.
  1123.  */
  1124. int PASCAL NEAR    mceq(bc, mt)
  1125. unsigned char bc;
  1126. MC    *mt;
  1127. {
  1128.     register int result;
  1129.  
  1130.     switch (mt->mc_type & MASKCLO) {
  1131.         case LITCHAR:
  1132.             result = (unsigned char) eq(bc, (unsigned char) mt->u.lchar);
  1133.             break;
  1134.  
  1135.         case ANY:
  1136.             result = (bc != '\r');
  1137.             break;
  1138.  
  1139.         case CCL:
  1140.             if (!(result = biteq(bc, mt->u.cclmap))) {
  1141.                 if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1142.                     (isletter(bc)))
  1143.                     result = biteq(chcase(bc), mt->u.cclmap);
  1144.             }
  1145.             break;
  1146.  
  1147.         case NCCL:
  1148.             result = !biteq(bc, mt->u.cclmap);
  1149.  
  1150.             if ((curwp->w_bufp->b_mode & MDEXACT) == 0 &&
  1151.                 (isletter(bc)))
  1152.                 result &= !biteq(chcase(bc), mt->u.cclmap);
  1153.  
  1154.             break;
  1155.  
  1156.         default:
  1157.             mlwrite(TEXT95, mt->mc_type);
  1158. /*                "%%mceq: what is %d?" */
  1159.             result = FALSE;
  1160.             break;
  1161.     }    /* End of switch.*/
  1162.  
  1163.     return (result);
  1164. }
  1165.  
  1166. /*
  1167.  * cclmake -- create the bitmap for the character class.
  1168.  *    ppatptr is left pointing to the end-of-character-class character,
  1169.  *    so that a loop may automatically increment with safety.
  1170.  */
  1171. int PASCAL NEAR    cclmake(ppatptr, mcptr)
  1172. char    **ppatptr;
  1173. MC    *mcptr;
  1174. {
  1175.     EBITMAP        bmap;
  1176.     register char    *patptr;
  1177.     register int    pchr, ochr;
  1178.  
  1179.     if ((bmap = (EBITMAP) malloc(BMAPSIZE)) == NULL) {
  1180.         mlabort(TEXT94);
  1181. /*            "%%Out of memory" */
  1182.         return FALSE;
  1183.     }
  1184.  
  1185.     memset(bmap, 0, BMAPSIZE);
  1186.  
  1187.     mcptr->u.cclmap = bmap;
  1188.     patptr = *ppatptr;
  1189.     ochr = MC_CCL;
  1190.  
  1191.     /*
  1192.      * Test the initial character(s) in ccl for
  1193.      * special cases - negate ccl, or an end ccl
  1194.      * character as a first character.  Anything
  1195.      * else gets set in the bitmap.
  1196.      */
  1197.     if (*++patptr == MC_NCCL) {
  1198.         patptr++;
  1199.         mcptr->mc_type = NCCL;
  1200.     }
  1201.     else
  1202.         mcptr->mc_type = CCL;
  1203.  
  1204.     if ((pchr = *patptr) == MC_ECCL) {
  1205.         mlwrite(TEXT96);
  1206. /*            "%%No characters in character class" */
  1207.         free(bmap);
  1208.         return FALSE;
  1209.     }
  1210.  
  1211.     while (pchr != MC_ECCL && pchr != '\0') {
  1212.         switch (pchr) {
  1213.             /* Range character loses its meaning if it is
  1214.              * the first or last character in the class.  We
  1215.              * also treat it as un-ordinary if the order is
  1216.              * wrong, e.g. "z-a".
  1217.              */
  1218.             case MC_RCCL:
  1219.                 pchr = *(patptr + 1);
  1220.                 if (ochr == MC_CCL || pchr == MC_ECCL ||
  1221.                     ochr > pchr)
  1222.                     setbit(MC_RCCL, bmap);
  1223.                 else
  1224.                 {
  1225.                     do {
  1226.                         setbit(++ochr, bmap);
  1227.                     } while (ochr < pchr);
  1228.                     patptr++;
  1229.                 }
  1230.                 break;
  1231.  
  1232.             /* Note: no break between case MC_ESC and the default.
  1233.              */
  1234.             case MC_ESC:
  1235.                 pchr = *++patptr;
  1236.             default:
  1237.                 setbit(pchr, bmap);
  1238.                 break;
  1239.         }
  1240.         ochr = pchr;
  1241.         pchr = *++patptr;
  1242.     }
  1243.  
  1244.     *ppatptr = patptr;
  1245.  
  1246.     if (pchr == '\0') {
  1247.         mlwrite(TEXT97);
  1248. /*            "%%Character class not ended" */
  1249.         free(bmap);
  1250.         return FALSE;
  1251.     }
  1252.     return TRUE;
  1253. }
  1254.  
  1255. /*
  1256.  * biteq -- is the character in the bitmap?
  1257.  */
  1258. int PASCAL NEAR    biteq(bc, cclmap)
  1259. int    bc;
  1260. EBITMAP    cclmap;
  1261. {
  1262.     if ((unsigned)bc >= HICHAR)
  1263.         return FALSE;
  1264.  
  1265.     return ( (*(cclmap + (bc >> 3)) & BIT(bc & 7))? TRUE: FALSE );
  1266. }
  1267.  
  1268. /*
  1269.  * setbit -- Set a bit (ON only) in the bitmap.
  1270.  */
  1271. VOID PASCAL NEAR setbit(bc, cclmap)
  1272. int    bc;
  1273. EBITMAP    cclmap;
  1274. {
  1275.     if ((unsigned)bc < HICHAR)
  1276.         *(cclmap + (bc >> 3)) |= BIT(bc & 7);
  1277. }
  1278. #endif
  1279.